package com.mystnihon.widget;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import com.mystnihon.library.R;

/**
 * @author Levi LIRVAT
 */
public class Overlay extends View {
    private static final int thumbDetectionMargin = 30;

    private Rect mHitBox;
    private float mThumbX; // Retain the thumb position.
    private float mStartX;
    private int mThumbWidth;
    private int mThumbHeight;
    private boolean mDragging = false;
    private Drawable mThumb;
    private Drawable mOverlay;
    private int mThumbOffset;
    // private float[] mMagneticPosition;

    private int mOverlayHeight;

    private OnSlideListener mOnSlideListener;

    public static interface OnSlideListener {
        public void onSlide(Overlay view, float thumbPosition, boolean fromUser);
    }

    /**
     * Default constructor
     * 
     * @param context
     */
    public Overlay(Context context) {
        super(context);
        init();
    }

    /**
     * Constructor
     * 
     * @param context
     * @param attrs
     */
    public Overlay(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    /**
     * Constructor
     * 
     * @param context
     * @param attrs
     * @param defStyle
     */
    public Overlay(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();

        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.Overlay);
        Drawable thumb = a.getDrawable(R.styleable.Overlay_thumb);
        if (thumb != null) {
            setThumb(thumb);
        } else {
            Log.e(VIEW_LOG_TAG, "The thumb is null");
        }
        Drawable overlay = a.getDrawable(R.styleable.Overlay_overlay);
        if (overlay != null) {
            setOverlay(overlay);
        } else {
            Log.e(VIEW_LOG_TAG, "The overlay is null");
        }
        a.recycle();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);
        final float x = event.getX();
        final float y = event.getY();
        mHitBox = getThumbHitBox();
        // Log.d(VIEW_LOG_TAG, String.format("Coord : (X %d, Y %d)", (int) x,
        // (int) y));

        if (mDragging) {
            if (isWithinRange(x)) {
                mThumbX = x;
            }
            updateOverlay();
        }
        final int action = event.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                if (mHitBox.contains((int) x, (int) y)) {
                    attemptClaimDrag();
                    mDragging = true;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (mDragging) {
                    if (mOnSlideListener != null) {
                        mOnSlideListener.onSlide(this, mThumbX, true);
                    }
                    invalidate();
                }
                break;
            case MotionEvent.ACTION_UP:
                mDragging = false;
                break;
            case MotionEvent.ACTION_CANCEL:
                mDragging = false;
                break;
        }
        if (mDragging)
            return true;
        else
            return false;
    }

    /**
     * Sets the thumb that will be drawn.
     * <p>
     * If the thumb is a valid drawable (i.e. not null), half its width will be
     * used as the new thumb offset (@see #setThumbOffset(int)).
     * 
     * @param thumb Drawable representing the thumb
     */
    public void setThumb(Drawable thumb) {
        boolean needUpdate;
        // This way, calling setThumb again with the same bitmap will result in
        // it recalcuating mThumbOffset (if for example it the bounds of the
        // drawable changed)
        if (mThumb != null && thumb != mThumb) {
            mThumb.setCallback(null);
            needUpdate = true;
        } else {
            needUpdate = false;
        }
        if (thumb != null) {
            thumb.setCallback(this);

            // Assuming the thumb drawable is symmetric, set the thumb offset
            // such that the thumb will hang halfway off either edge of the
            // progress bar.
            mThumbOffset = thumb.getIntrinsicWidth() / 2;

            // If we're updating get the new states
            if (needUpdate && (thumb.getIntrinsicWidth() != mThumb.getIntrinsicWidth() || thumb.getIntrinsicHeight() != mThumb.getIntrinsicHeight())) {
                requestLayout();
            }
        }
        mThumb = thumb;
        invalidate();

        if (needUpdate) {
            updateThumb(getWidth(), getHeight());
            if (thumb.isStateful()) {
                // Note that if the states are different this won't work.
                // For now, let's consider that an app bug.
                int[] state = getDrawableState();
                thumb.setState(state);
            }
        } else {
            int thumbWidth = thumb.getIntrinsicWidth();
            int thumbHeight = thumb.getIntrinsicHeight();
            mThumbHeight = thumbHeight;
            mThumbWidth = thumbWidth;
            // The extra space for the thumb to move on the track
            mThumb.setBounds(0, 0, mThumbWidth, mThumbHeight);
        }
    }

    /**
     * The specified amount to move the thumb drawable.
     * 
     * @param offset Offset to draw the thumb.
     */
    public void setThumbOffset(int offset) {
        mThumbOffset = offset;
    }

    /**
     * @param overlay Drawable to use as overlay
     */
    public void setOverlay(Drawable overlay) {
        mOverlay = overlay;
        updateOverlay();
    }

    /**
     * Define the start of the overlay.
     * 
     * @param positionX the start position of the overlay.
     */
    public void setStartPosition(float positionX) {
        // if (positionX >= getLeft() && positionX <= mThumbX) {
        mStartX = positionX;
        updateOverlay();
        invalidate();
        // }
    }

    /**
     * Set the thmb position.
     * 
     * @param thumbPos the thumb position
     */
    public void setThumbPosition(float thumbPos) {
        // test if thumb position are within bounds.
        if (isWithinRange(thumbPos)) {
            mThumbX = thumbPos;
            updateOverlay();
            invalidate();
            if (mOnSlideListener != null) {
                mOnSlideListener.onSlide(this, mThumbX, false);
            }
        }
    }

    public float getThumbPos() {
        return mThumbX;
    }

    public float getStartPos() {
        return mStartX;
    }

    public void setOnSlideListener(OnSlideListener lOnSlideListener) {
        mOnSlideListener = lOnSlideListener;
    }

    protected float getViewMiddle() {
        return (getHeight()) / 2;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int dw = resolveSize(0, widthMeasureSpec);
        int height = Math.max(mThumbHeight, mOverlayHeight) + getPaddingTop() + getPaddingBottom();
        final int dh = resolveSize(height, heightMeasureSpec);
        setMeasuredDimension(dw, dh);
    }

    @Override
    protected void drawableStateChanged() {
        super.drawableStateChanged();

        if (mThumb != null && mThumb.isStateful()) {
            int[] state = getDrawableState();
            mThumb.setState(state);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        canvas.save();
        // Draw the rubber.
        if (mOverlay != null) {
            canvas.translate(mStartX, getViewMiddle() - mOverlayHeight / 2);
            mOverlay.draw(canvas);
            canvas.restore();
        }
        // Draw the thumb.
        if (mThumb != null) {
            canvas.translate(mThumbX - mThumbOffset, getViewMiddle() - mThumbHeight / 2);
            mThumb.draw(canvas);
        }
        canvas.restore();

    }

    protected Rect getThumbHitBox() {

        final int left = (int) mThumbX - thumbDetectionMargin;
        final int right = left + mThumbWidth + thumbDetectionMargin;
        final int bottom = (int) getViewMiddle() + mThumbHeight / 2;
        final int top = (int) getViewMiddle() - mThumbHeight / 2;
        return new Rect(left, top, right, bottom);
    }

    /**
     * Tries to claim the user's drag motion, and requests disallowing any
     * ancestors from stealing events in the drag.
     */
    private void attemptClaimDrag() {
        if (getParent() != null) {
            getParent().requestDisallowInterceptTouchEvent(true);
        }
    }

    private void updateThumb(int width, int height) {
        Drawable thumb = mThumb;

        int available = width - getPaddingLeft() - getPaddingRight();
        // The drawable width will be the intrinsicWidth. the drawable height
        // will be the min of the drawable

        int thumbWidth = thumb.getIntrinsicWidth();
        int thumbHeight = thumb.getIntrinsicHeight();
        mThumbHeight = thumbHeight;
        mThumbWidth = Math.min(available, thumbWidth);

        // The extra space for the thumb to move on the track
        mThumb.setBounds(0, 0, mThumbWidth, mThumbHeight);
    }

    private void updateOverlay() {
        final int iHeight = mOverlay.getIntrinsicHeight();// Math.min(getHeight(),
                                                          // );
        mOverlayHeight = iHeight;
        mOverlay.setBounds(0, 0, (int) (mThumbX - mStartX), mOverlayHeight);
    }

    private boolean isWithinRange(float value) {
        return value <= getWidth() && value >= mStartX;
    }

    private void init() {
        mStartX = 0f;
        mThumbX = 0f;
    }

}
